package cn.virens.server.netty_tcp;

import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.hutool.core.thread.ThreadUtil;
import cn.virens.app.AppConfig;
import cn.virens.app.cmpt.Component;
import cn.virens.server.netty_tcp.handler.PacketHandler;
import cn.virens.server.netty_tcp.packet.TcpWritePacket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;

public class TCPServerManagerImpl implements TCPServerManager, Component {
	private final Logger logger = LoggerFactory.getLogger(TCPServerManagerImpl.class);

	private final ServerBootstrap mBootstrap = new ServerBootstrap();

	private final EventLoopGroup mEventLoopGroup = new NioEventLoopGroup();
	private final EventExecutorGroup mExecutorGroup = new DefaultEventExecutorGroup(2);

	private final TCPServerPacketHandler handler = new TCPServerPacketHandler();

	private ChannelFuture channelFuture;
	private Integer port;

	@Override
	public void initialize(AppConfig config) throws Exception {
		ThreadUtil.newThread(() -> {
			try {
				this.mBootstrap.group(mEventLoopGroup);
				this.mBootstrap.channel(NioServerSocketChannel.class);
				this.mBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
				this.mBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
				this.mBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(15, 0, 0, TimeUnit.MINUTES));
						ch.pipeline().addLast("datagramPacketDecoder", new TCPServerPacketDecoder(handler));
						ch.pipeline().addLast("datagramPacketEncoder", new TCPServerPacketEncoder(handler));
						ch.pipeline().addLast(mExecutorGroup, "handler", handler);
					}
				});

				this.port = config.getInteger("netty.tcp.port");
				this.channelFuture = mBootstrap.bind(port).sync();
				this.channelFuture.channel().closeFuture().await();
			} catch (Exception e) {
				logger.error("启动失败:" + e.getMessage(), e);
			} finally {
				logger.debug("Netty TCP Server is stoped!");
			}
		}, "Netty TCP Server Thread", true).start();
	}

	@Override
	public void destroy() throws Exception {
		logger.debug("The TCP Server Manager stoping...");

		this.mExecutorGroup.shutdownGracefully();
		this.mEventLoopGroup.shutdownGracefully();
		this.channelFuture.channel().close().sync();
	}

	@Override
	public TCPServerChannel poolRm(String mac) {
		return handler.poolRm(mac);
	}

	@Override
	public TCPServerChannel poolGet(String mac) {
		return handler.poolGet(mac);
	}

	@Override
	public void poolEach(Consumer<TCPServerChannel> action) {
		this.handler.poolEach(action);
	}

	public void addHandler(PacketHandler handler) {
		this.handler.addHandler(handler);
	}

	@Override
	public boolean write(TcpWritePacket packet) {
		return handler.write(packet.channel(), packet);
	}
}
